﻿//
// Copyright (c) 2009-2010 Intrepid Services, LLC.  All rights reserved.
// Use, duplication, or modification subject to license, consulting, and/or employment agreement(s).
//
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;


namespace ConstructionBidOnline.Biz
{
	/// <summary>
	/// Provides properties, methods, and events for the management of the run-time lookup cache.
	/// </summary>
	public sealed partial class LookupCacheManager
	{
		#region Constants

		// Name of the session variable that holds the lookups.
		public static readonly string SESSION_KEY = "__LookupCacheManager";

		#endregion

		#region Construction / Disposal

		public LookupCacheManager() { }

		#endregion

		#region Public Methods

		public static void Clear()
		{
			// Reset all of the Lookup objects.
			Cache.Clear();
		}

		#endregion

		#region Privates

		/// <summary>
		/// Returns true iff the current application is a web application.
		/// </summary>
		private static bool IsWebApp
		{
			get { return (System.Web.HttpContext.Current != null); }
		}


		/// <summary>
		/// Provides the lookup cache's run-time storage.
		/// </summary>
		private static System.Collections.Specialized.HybridDictionary Cache
		{
			get
			{
				if (IsWebApp)
				{
					// Web: Session stuff goes in session.
					if (System.Web.HttpContext.Current.Session[SESSION_KEY] == null)
						System.Web.HttpContext.Current.Session[SESSION_KEY] = new HybridDictionary(256, false);

					return ((HybridDictionary)System.Web.HttpContext.Current.Session[SESSION_KEY]);
				}
				else
				{
					// Smart client: Session stuff goes in static member.
					if (_session == null)
						_session = new HybridDictionary(256, false);

					return (_session);
				}
			}
		} private static HybridDictionary _session = null;

		#endregion
	}


	[FlagsAttribute]
	public enum ElementFilters
	{
		#region Filter Flags

		Active = 0x00000001,

		Inactive = 0x00000002,

		ActiveOrInactive = Active | Inactive,

		Selectable = 0x00000004,

		NotSelectable = 0x00000008,

		AdditionalInfoRequired = 0x00000010,

		NoAdditionalInfoRequired = 0x00000020,

		Unfiltered = 0x0FFFFFFF

		#endregion
	}


	/// <summary>
	/// Base class containing the common properties of all Intrepid Services-style lookup tables.
	/// </summary>
	public abstract class LookupElement
	{
		#region Construction / Disposal

		public LookupElement(int value, string text, int displayOrder, string version, bool active)
		{
			Value = value;
			Text = text;
			DisplayOrder = displayOrder;
			Version = version;
			IsActive = active;
		}


		public LookupElement(string description, int displayOrder, bool additionalInfoRequired, string version, bool active /*, bool isSelectable*/)
		{
			Description = description;
			DisplayOrder = displayOrder;
			AdditionalInfoRequired = additionalInfoRequired;
			Version = version;
			IsActive = active;
			//IsSelectable = isSelectable;
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// The PKID of the lookup element.
		/// </summary>
		public int Value
		{
			get { return (_value); }
			set { _value = value; }
		} private int _value = 0;


		/// <summary>
		/// The Text of the lookup element.
		/// </summary>
		public string Text
		{
			get { return (_text); }
			set { _text = value; }
		} private string _text = null;


		/// <summary>
		/// The Description of the lookup element.
		/// </summary>
		public string Description
		{
			get { return (_description); }
			set { _description = value; }
		} private string _description = null;


		/// <summary>
		/// The ordinal position in which this lookup element should be displayed in user-interface elements.
		/// </summary>
		public int DisplayOrder
		{
			get { return (_displayOrder); }
			set { _displayOrder = value; }
		} private int _displayOrder = -1;


		/// <summary>
		/// True iff this lookup element requires that additional information be collected by the UI.
		/// </summary>
		public bool AdditionalInfoRequired
		{
			get { return (_additionalInfoRequired); }
			set { _additionalInfoRequired = value; }
		} private bool _additionalInfoRequired = false;


		/// <summary>
		/// Optional: The Version number of the application to which this lookup element applies.
		/// </summary>
		public string Version
		{
			get { return (_version); }
			set { _version = value; }
		} private string _version = null;


		/// <summary>
		/// True iff this lookup element is considered to be in-use.
		/// </summary>
		public bool IsActive
		{
			get { return (_isActive); }
			set { _isActive = value; }
		} private bool _isActive = true;


		/// <summary>
		/// Indicates that this lookup element should not be selectable as a value for a new object or to change/update the value of an existing object.
		/// The customary reason for this value to be False is that the lookup element is from a prior version of the application (in other words, it's 
		/// a value that used to be valid but is no longer used.)
		/// </summary>
		public bool IsSelectable
		{
			get { return (_isSelectable); }
			set { _isSelectable = value; }
		} private bool _isSelectable = true;

		#endregion

		#region Public Methods

		#region ToString

		/// <summary>
		/// User-friendly string representation of this object.
		/// </summary>
		public override string ToString()
		{
			return (String.Format("Value={0}, Text={1}, Description={2}, DisplayOrder={3}, AdditionalInfoRequired={4}, Version={5}, Active={6}, IsSelectable={7}",
				Value,
				Text ?? "null",
				Description ?? "null",
				DisplayOrder,
				AdditionalInfoRequired.ToString(),
				Version ?? "null",
				IsActive.ToString(),
				IsSelectable));
		}

		#endregion

		#endregion
	}


	/// <summary>
	/// Generic class used as the base for specific lookups.
	/// Implements the common functionality for all Lookup-derived classes.
	/// </summary>
	public abstract class Lookup<T> : List<T> where T : LookupElement
	{
		#region Public Methods

		#region Select

		/// <summary>
		/// Returns a collection of elements filtered by the attributes specified in 'flags'.
		/// </summary>
		public virtual List<T> Select(ElementFilters flags)
		{
			return Select(flags, null);
		}


		/// <summary>
		/// Returns a collection of elements filtered by the attributes specified in 'flags' and, optionally, 'version'.
		/// </summary>
		protected virtual List<T> Select(ElementFilters flags, string version)
		{
			List<T> list = new List<T>();

			if ((flags & ElementFilters.Active) == ElementFilters.Active)
			{
				foreach (T item in this)
				{
					if (item.IsActive)
						list.Add(item);
				}
			}

			if ((flags & ElementFilters.Inactive) == ElementFilters.Inactive)
			{
				foreach (T item in this)
				{
					if (!item.IsActive)
						if (!list.Contains(item))
							list.Add(item);
				}
			}

			if ((flags & ElementFilters.Selectable) == ElementFilters.Selectable)
			{
				foreach (T item in this)
				{
					if (item.IsSelectable)
						if (!list.Contains(item))
							list.Add(item);
				}
			}

			if ((flags & ElementFilters.NotSelectable) == ElementFilters.NotSelectable)
			{
				foreach (T item in this)
				{
					if (!item.IsSelectable)
						if (!list.Contains(item))
							list.Add(item);
				}
			}

			if ((flags & ElementFilters.AdditionalInfoRequired) == ElementFilters.AdditionalInfoRequired)
			{
				foreach (T item in this)
				{
					if (item.AdditionalInfoRequired)
						if (!list.Contains(item))
							list.Add(item);
				}
			}

			if ((flags & ElementFilters.NoAdditionalInfoRequired) == ElementFilters.NoAdditionalInfoRequired)
			{
				foreach (T item in this)
				{
					if (!item.AdditionalInfoRequired)
						if (!list.Contains(item))
							list.Add(item);
				}
			}


			if (version != null)
			{
				foreach (T item in list)
				{
					if (item.Version != version)
						list.Remove(item);
				}
			}

			return (list);
		}


		/// <summary>
		/// Returns a collection of elements filtered by the specified predicate function.
		/// </summary>
		public virtual List<T> Select(Predicate<T> match)
		{
			List<T> list = this.FindAll(match);
			return (list);
		}


		/// <summary>
		/// Returns a collection of elements whose text starts with 'text'. The comparison is case-insensitive.
		/// </summary>
		public virtual List<T> Select(string text)
		{
			List<T> list = new List<T>();

			if ((text == null) || (text.Length == 0))
				return (list);

			foreach (T item in this)
				if ((item.Text != null) && item.Text.ToLower().StartsWith(text.ToLower()))
					list.Add(item);

			return (list);
		}

		#endregion

		#region ToString

		/// <summary>
		/// User-friendly string representation of this object's contents.
		/// </summary>
		public override string ToString()
		{
			StringBuilder sb = new StringBuilder(2048);
			foreach (object obj in this)
				sb.AppendFormat("Lookup<{0}>: {1}\n", typeof(T).ToString(), obj.ToString());

			return (sb.ToString());
		}

		#endregion

		#region This-From-That Helpers

		/// <summary>
		/// Returns the Text of the first item from the collection where item.Value == value.
		/// </summary>
		public string TextFromValue(int value)
		{
			foreach (T item in this)
			{
				if (item.Value == value)
					return (item.Text);
			}

			return (null);
		}

		#endregion

		#endregion

		#region Required non-implemented stubs: Load, LoadOneByID

		/// <summary>
		/// Loads this collection of LookupElements.
		/// </summary>
		public abstract void Load(string version);


		/// <summary>
		/// Intended to be used when a value is encountered that is not already part of the collection (similar to a cache miss).
		/// </summary>
		public abstract T LoadOneByID(int pkid);

		#endregion

		#region Optional non-implemented stubs: Insert, Update, Delete

		/// <summary>
		/// Inserts the specified LookupElement into the database.
		/// </summary>
		public virtual int Insert(LookupElement elem)
		{
			throw new NotImplementedException("No default implementation; derived class must override this method.");
		}


		/// <summary>
		/// Updates the specified LookupElement's database representation.
		/// </summary>
		public virtual int Update(LookupElement elem)
		{
			throw new NotImplementedException("No default implementation; derived class must override this method.");
		}


		/// <summary>
		/// Deletes the specified LookupElement from the database.
		/// </summary>
		public virtual int Delete(LookupElement elem)
		{
			throw new NotImplementedException("No default implementation; derived class must override this method.");
		}

		#endregion
	}
}
